﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using Csla;
using Csla.Core;

namespace OpenExpressApp
{
    public static class ExtensionMethods
    {
        /// <summary>
        /// 复制粘贴在客户端执行单条记录，所以使用反射性能不会有问题。
        /// </summary>
        /// <param name="dest"></param>
        /// <param name="src"></param>
        public static void AssignFrom(this BusinessBase dest, BusinessBase src)
        {
            if (dest == null) throw new ArgumentNullException("dest");
            if (src == null) throw new ArgumentNullException("src");
            var type = dest.GetType();
            if (!(type == src.GetType())) throw new Exception("类型不一致!");

            BusinessObjectInfo boInfo = ApplicationModel.GetBusinessObjectInfo(type);

            #region 先拷贝所有的一般属性

            foreach (BusinessObjectPropertyInfo propInfo in boInfo.BOPropertyInfos)
            {
                if (propInfo.RuntimeProperty.CanWrite == false
                    || propInfo.NotCopy
                    || propInfo.Name == "Id"
                    )
                {
                    continue;
                }
                string propertyName = propInfo.Name;

                //拷贝值
                var value = src.GetPropertyValue(propertyName);
                dest.SetPropertyValue(propertyName, value);

                //如果是lookup属性，则把LookupProperty也相应拷贝
                if (propInfo.IsLookup)
                {
                    string lookupPropertyName = propInfo.LookupAttribute.LookupPropertyName;
                    if (string.IsNullOrEmpty(lookupPropertyName) == false)
                    {
                        value = src.GetPropertyValue(lookupPropertyName);
                        dest.SetPropertyValue(lookupPropertyName, value);
                    }
                }
            }

            #endregion

            #region 拷贝所有的关联属性

            foreach (BusinessObjectsPropertyInfo propInfo in boInfo.BOsPropertyInfos)
            {
                if (propInfo.NotCopy)
                {
                    continue;
                }

                //IBindingList childs = propInfo.PropertyType.GetMethod("NewChild").Invoke(null, null) as IBindingList;
                var destList = dest.GetPropertyValue(propInfo.Name) as IBindingList;

                //新旧节点映射，以便拷贝Pid
                var nodeMap = new Dictionary<BusinessBase, BusinessBase>();

                //srcList必须保证已经按照树形结构进行了排序
                var srcList = src.GetPropertyValue(propInfo.Name) as IList;
                foreach (BusinessBase oldItem in srcList)
                {
                    AddChild(dest, destList, oldItem, nodeMap);
                }

                foreach (BusinessBase oldItem in srcList)
                {
                    //给Pid赋值
                    var oldNode = oldItem as ITreeNode;
                    if (oldNode != null && oldNode.Pid != null)
                    {
                        //找到oldNode的ParentNode
                        BusinessBase oldParent = null;
                        foreach (ITreeNode item in srcList)
                        {
                            if (item.Id.Equals(oldNode.Pid))
                            {
                                oldParent = item as BusinessBase;
                                break;
                            }
                        }

                        //如果找到OldParent，则把newNode的ParentNode设置为OldParent对应的NewParent对象。
                        if (oldParent != null)
                        {
                            var newNode = nodeMap[oldItem] as ITreeNode;
                            var newParent = nodeMap[oldParent] as ITreeNode;
                            newNode.ParentNode = newParent;
                            //以下方式在没有pid设置方法时，会出现bug
                            //var idValue = (nodeMap[oldParent] as ITreeNode).Id;
                            //newNode.SetPropertyValue("Pid", idValue);
                        }
                    }
                }
            }

            #endregion
        }

        /// <summary>
        /// 找到view.BOType中的某个属性名。
        /// 这个属性标记了Lookup且它的LookupType是view的父视图的BOType
        /// </summary>
        /// <param name="view"></param>
        /// <returns></returns>
        public static string GetKeyToParentView(this ObjectView view)
        {
            foreach (var info in ApplicationModel.GetBusinessObjectInfo(view.BOType).BOPropertyInfos)
            {
                if (info.IsLookup && (info.LookupAttribute.LookupType == view.Parent.BOType))
                {
                    return info.Name;
                }
            }
            return null;
        }
        /// <summary>
        /// 查找子对象(childObjectType)在树(currView.Data)中的外键名称.
        /// </summary>
        /// <param name="view"></param>
        /// <returns></returns>
        public static string GetChildForeignKey(this ObjectView currView, Type childObjectType)
        {
            foreach (var info in ApplicationModel.GetBusinessObjectInfo(childObjectType).BOPropertyInfos)
            {
                if (info.IsLookup && (info.LookupAttribute.LookupType == currView.BOType))
                {
                    return info.Name;
                }
            }
            return null;
        }

        /// <summary>
        /// 查找子对象(childObjectType)在树(currView.Data)中的父对象名称.
        /// </summary>
        /// <param name="currView"></param>
        /// <param name="newObject"></param>
        /// <returns></returns>
        public static string GetChildForeignParent(this ObjectView currView, Type childObjectType)
        {
            foreach (var info in ApplicationModel.GetBusinessObjectInfo(childObjectType).BOPropertyInfos)
            {
                if (info.IsLookup && (info.LookupAttribute.LookupType == currView.BOType))
                {
                    return info.LookupAttribute.LookupPropertyName;
                }
            }
            return null;
        }

        /// <summary>
        /// 找到view.BOType中的某个标记了Lookup的属性的LookupPropertyName所对应的属性。
        /// 这个属性标记了Lookup且它的LookupType是view的父视图的BOType
        /// </summary>
        /// <param name="view"></param>
        /// <returns></returns>
        public static string GetKeyObjToParentView(this ObjectView view)
        {
            foreach (var info in ApplicationModel.GetBusinessObjectInfo(view.BOType).BOPropertyInfos)
            {
                if (info.IsLookup && (info.LookupAttribute.LookupType == view.Parent.BOType))
                {
                    return info.LookupAttribute.LookupPropertyName;
                }
            }
            return null;
        }

        public static void AttachBehavior(this ObjectView view, IViewBehavior behavior)
        {
            behavior.View = view;
            behavior.Attach();
        }

        private static void AddChild(BusinessBase dest, IBindingList destList, BusinessBase oldChild, Dictionary<BusinessBase, BusinessBase> nodeMap)
        {
            if (nodeMap.ContainsKey(oldChild) == false)
            {
                var newChild = destList.AddNew() as BusinessBase;
                newChild.AssignFrom(oldChild);
                nodeMap.Add(oldChild, newChild);

                var boInfo = ApplicationModel.GetBusinessObjectInfo(oldChild.GetType());
                foreach (BusinessObjectPropertyInfo propInfo in boInfo.BOPropertyInfos)
                {
                    //给当前对象细表的主表Lookup属性赋值
                    if (propInfo.IsLookup)
                    {
                        string lookupPropertyName = propInfo.LookupAttribute.LookupPropertyName;
                        if (string.IsNullOrEmpty(lookupPropertyName) == false)
                        {
                            if (propInfo.LookupAttribute.LookupType == dest.GetType())
                            {
                                //ProjectContractSubjects的Lookup属性对象字段获取时使用了Id,所以先给Id赋值
                                object id = dest.GetPropertyValue("Id");
                                newChild.SetPropertyValue(propInfo.Name, id);
                                //如果是当前表的主表,则直接赋值,不通过属性赋值
                                newChild.SetPropertyValue(lookupPropertyName, dest);
                            }
                        }
                    }
                }
            }
        }
    }
}